package com.rex.saas.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.rex.saas.bean.*;
import com.rex.saas.bean.request.GatewayAddRequest;
import com.rex.saas.bean.response.CategroyResponse;
import com.rex.saas.constants.Constants;
import com.rex.saas.db.entity.*;
import com.rex.saas.db.mapper.*;
import com.rex.saas.enums.DeviceTypeEnum;
import com.rex.saas.enums.GwStatusEnum;
import com.rex.saas.utils.JwtUtils;
import com.rex.saas.utils.RandomGeneratorUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @ClassName DeviceService
 * @Description:
 * @Author: zhusiyu
 * @CreateDate: 2024/12/18 11:06	//创建时间
 */
@Component
@Slf4j
public class GatewayService {
    @Autowired
    private SaasDeviceInfoMapper deviceInfoMapper;

    @Autowired
    private PaasGatewayCredentialsMapper paasGatewayCredentialsMapper;

    @Autowired
    private SaasGatewayDeviceRelationMapper saasGatewayDeviceRelationMapper;


    @Autowired
    private PaasGatewayDeviceRelationMapper paasGatewayDeviceRelationMapper;

    @Autowired
    private SaasSpaceDeviceRelationMapper saasSpaceDeviceRelationMapper;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private SpaceService spaceService;

    @Autowired
    private RexolarApiClient rexolarApiClient;

    public String[] getHead() {
        return new String[]{"gatewayName", "clientId", "username","password"};
    }

    public List<String[]> getUnusedGw(String tenantId) {
        LambdaQueryWrapper<PaasGatewayCredentials> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(PaasGatewayCredentials::getTenantId, tenantId);
        queryWrapper.eq(PaasGatewayCredentials::getStatus, GwStatusEnum.CREATED.getCode());
        List<PaasGatewayCredentials> list = paasGatewayCredentialsMapper.selectList(queryWrapper);
        return list.stream().map(item -> new String[]{item.getGwName(), item.getClientId(), item.getUsername(), item.getPassword()}).collect(Collectors.toList());
    }

    public String getUnusedGwName() {
        return "unusedGw";
    }

    public List<PaasGatewayDeviceRelation> getGwByDeviceName(String deviceName, String tenantId) {
        LambdaQueryWrapper<PaasGatewayDeviceRelation> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(PaasGatewayDeviceRelation::getDeviceName, deviceName);
        queryWrapper.eq(PaasGatewayDeviceRelation::getTenantId, tenantId);
        return paasGatewayDeviceRelationMapper.selectList(queryWrapper);
    }
    public List<PaasGatewayCredentials> getGwByName(String gwName) {
        LambdaQueryWrapper<PaasGatewayCredentials> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(PaasGatewayCredentials::getGwName, gwName);
        queryWrapper.ne(PaasGatewayCredentials::getStatus, GwStatusEnum.DELETED.getCode());
        return paasGatewayCredentialsMapper.selectList(queryWrapper);
    }

    public List<PaasGatewayCredentials> getGwByClientId(String clientId) {
        LambdaQueryWrapper<PaasGatewayCredentials> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(PaasGatewayCredentials::getClientId, clientId);
        queryWrapper.ne(PaasGatewayCredentials::getStatus, GwStatusEnum.DELETED.getCode());
        return paasGatewayCredentialsMapper.selectList(queryWrapper);
    }

    public boolean importGw(List<PaasGatewayCredentials> paasGatewayCredentials, String token) {
        CategroyResponse categroyResponse =categoryService.getDetailsByName(token, Constants.GW_ZIGBEE);
        for (PaasGatewayCredentials gw : paasGatewayCredentials){
            DynamicCreateDevice device = new DynamicCreateDevice();
            DynamicCreateDevice.DeviceBean deviceBean = new DynamicCreateDevice.DeviceBean();
            DynamicCreateDevice.DeviceProfileId deviceProfileId = new DynamicCreateDevice.DeviceProfileId();
            DynamicCreateDevice.Credentials mqttBasicCredentials = new DynamicCreateDevice.Credentials();
            DynamicCreateDevice.AdditionalInfo additionalInfo = new DynamicCreateDevice.AdditionalInfo();

            deviceBean.setName(gw.getGwName());
            deviceBean.setLabel(gw.getGwAlias());
            deviceProfileId.setId(categroyResponse.getCategoryId());
            deviceBean.setDeviceProfileId(deviceProfileId);
            additionalInfo.setGateway(true);
            additionalInfo.setOverwriteActivityTime(true);
            deviceBean.setAdditionalInfo(additionalInfo);

            DynamicCreateDevice.CredentialsValue credentialsValue = new DynamicCreateDevice.CredentialsValue();
            credentialsValue.setClientId(gw.getClientId());
            if(StringUtils.isBlank(gw.getUsername())){
                String username = RandomGeneratorUtils.generateRandomString(10);
                credentialsValue.setUserName(username);
            }else {
                credentialsValue.setUserName(gw.getUsername());
            }
            if(StringUtils.isBlank(gw.getPassword())){
                String password = RandomGeneratorUtils.generateRandomString(20);
                credentialsValue.setPassword(password);
            }else {
                credentialsValue.setPassword(gw.getPassword());
            }


            mqttBasicCredentials.setCredentialsValue(JSON.toJSONString(credentialsValue));

            device.setDevice(deviceBean);
            device.setCredentials(mqttBasicCredentials);

            String data = JSON.toJSONString(device);
            String coreId = rexolarApiClient.dynamicCreateDevice(data, token);
            if(coreId==null){
                log.error("importGw dynamicCreateDevice error, data:{}", data);
                return false;
            }
            PaasGatewayCredentials entity = new PaasGatewayCredentials();
            entity.setGwId(coreId);
            entity.setGwName(gw.getGwName());
            entity.setGwAlias(gw.getGwAlias());
            entity.setClientId(credentialsValue.getClientId());
            entity.setUsername(credentialsValue.getUserName());
            entity.setPassword(credentialsValue.getPassword());
            entity.setTenantId(JwtUtils.getTenantId(token));
            entity.setStatus(GwStatusEnum.CREATED.getCode());
            entity.setProfileId(categroyResponse.getCategoryId());
            Date createTime = new Date();
            entity.setGmtCreate(createTime);
            entity.setGmtModified(createTime);

            if(paasGatewayCredentialsMapper.insert(entity)!=1){
                log.error("importGw gatewayCredentialsService save error, data:{}", data);
                return false;
            }
        }
        return true;
    }

    public void deleteGw(String gwName) {
        paasGatewayCredentialsMapper.delete(new LambdaQueryWrapper<PaasGatewayCredentials>()
                .eq(PaasGatewayCredentials::getGwName, gwName));
    }

    public Long add(GatewayAddRequest gatewayAddRequest, String token) {
        CategroyResponse categroyResponse =categoryService.getDetailsByName(token, Constants.GW_ZIGBEE);
        if (categroyResponse==null){
            log.error("add gatewayCredentialsService getDetailsByName error, deviceName:{}", Constants.GW_ZIGBEE);
            return null;
        }
        SaasSpaceInfo space = spaceService.getSpaceById(gatewayAddRequest.getSpaceId());
        if(space==null){
            log.error("add gatewayCredentialsService getSpaceById error, spaceId:{}", gatewayAddRequest.getSpaceId());
            return null;
        }
        DeviceBasicData deviceBasicData = rexolarApiClient.getDeviceByDeviceName(gatewayAddRequest.getGwName(), token);
        String coreId = null;
        if (deviceBasicData == null) {
            DynamicCreateDevice device = new DynamicCreateDevice();
            DynamicCreateDevice.DeviceBean deviceBean = new DynamicCreateDevice.DeviceBean();
            DynamicCreateDevice.DeviceProfileId deviceProfileId = new DynamicCreateDevice.DeviceProfileId();
            DynamicCreateDevice.Credentials mqttBasicCredentials = new DynamicCreateDevice.Credentials();
            DynamicCreateDevice.AdditionalInfo additionalInfo = new DynamicCreateDevice.AdditionalInfo();

            deviceBean.setName(gatewayAddRequest.getGwName());
            deviceBean.setLabel(gatewayAddRequest.getGwAlias());
            deviceProfileId.setId(categroyResponse.getCategoryId());
            deviceBean.setDeviceProfileId(deviceProfileId);
            additionalInfo.setGateway(true);
            additionalInfo.setOverwriteActivityTime(true);
            deviceBean.setAdditionalInfo(additionalInfo);

            DynamicCreateDevice.CredentialsValue credentialsValue = new DynamicCreateDevice.CredentialsValue();
            credentialsValue.setClientId(gatewayAddRequest.getClientId());
            credentialsValue.setUserName(gatewayAddRequest.getUsername());
            credentialsValue.setPassword(gatewayAddRequest.getPassword());
            mqttBasicCredentials.setCredentialsValue(JSON.toJSONString(credentialsValue));
            device.setDevice(deviceBean);
            device.setCredentials(mqttBasicCredentials);
            String data = JSON.toJSONString(device);
            coreId = rexolarApiClient.dynamicCreateDevice(data, token);
            if (StringUtils.isBlank(coreId)){
                log.error("generateDevice dynamicCreateDevice error, data:{}", data);
                return null;
            }
        }else {
            coreId = deviceBasicData.getCoreId();
        }
        PaasGatewayCredentials paasGatewayCredentials = new PaasGatewayCredentials();
        BeanUtils.copyProperties(gatewayAddRequest, paasGatewayCredentials);
        paasGatewayCredentials.setGwAlias(gatewayAddRequest.getGwAlias()==null?gatewayAddRequest.getGwName():gatewayAddRequest.getGwAlias());
        paasGatewayCredentials.setStatus(GwStatusEnum.CREATED.getCode());
        paasGatewayCredentials.setGmtCreate(new Date());
        paasGatewayCredentials.setGmtModified(new Date());
        paasGatewayCredentials.setTenantId(JwtUtils.getTenantId( token));
        paasGatewayCredentials.setProfileId(categroyResponse.getCategoryId());
        paasGatewayCredentials.setGwId(coreId);
        if(paasGatewayCredentialsMapper.insert(paasGatewayCredentials)!=1){
            log.error("add gatewayCredentialsService save error, data:{}", paasGatewayCredentials);
            return null;
        }

        boolean result = rexolarApiClient.addRelationsInfo(token, space.getCoreId(), "ASSET", "Contains", coreId, "DEVICE");
        if(!result){
            log.error("add addRelationsInfo error, gatewayAddRequest:{}", gatewayAddRequest);
            return null;
        }
        SaasSpaceDeviceRelation saasSpaceDeviceRelation = new SaasSpaceDeviceRelation();
        saasSpaceDeviceRelation.setTenantId(JwtUtils.getTenantId( token));
        saasSpaceDeviceRelation.setCustomerId(JwtUtils.getCustomerId( token));
        saasSpaceDeviceRelation.setDeviceName(gatewayAddRequest.getGwName());
        saasSpaceDeviceRelation.setSpaceInfoId(gatewayAddRequest.getSpaceId());
        saasSpaceDeviceRelation.setType(DeviceTypeEnum.GATEWAY.getCode());
        saasSpaceDeviceRelation.setSpaceId(space.getCoreId());
        saasSpaceDeviceRelation.setPosition(gatewayAddRequest.getPosition());
        if(saasSpaceDeviceRelationMapper.insert(saasSpaceDeviceRelation)!=1){
            log.error("add insert saasSpaceDeviceRelation error, saasSpaceDeviceRelation:{}", saasSpaceDeviceRelation);
        }
        return paasGatewayCredentials.getId();
    }

    public List<GatewayInfo> list(Long spaceId) {
        List<GatewayInfo> gatewayInfos = new ArrayList<>();
        List<SaasSpaceDeviceRelation> saasSpaceDeviceRelations = saasSpaceDeviceRelationMapper
                .selectList(new LambdaQueryWrapper<SaasSpaceDeviceRelation>().eq(SaasSpaceDeviceRelation::getSpaceInfoId, spaceId)
                        .eq(SaasSpaceDeviceRelation::getType, DeviceTypeEnum.GATEWAY.getCode()));
        for (SaasSpaceDeviceRelation saasSpaceDeviceRelation : saasSpaceDeviceRelations){
            List<PaasGatewayCredentials> paasGatewayCredentials = paasGatewayCredentialsMapper.selectList(
                    new LambdaQueryWrapper<PaasGatewayCredentials>().eq(PaasGatewayCredentials::getGwName, saasSpaceDeviceRelation.getDeviceName()));
            if (CollectionUtils.isEmpty(paasGatewayCredentials)){
                log.error("paasGatewayCredentials is null, GatewayInfo:{}", saasSpaceDeviceRelation.getDeviceName());
                continue;
            }
            GatewayInfo gatewayInfo = new GatewayInfo();
            BeanUtils.copyProperties(paasGatewayCredentials.get(0), gatewayInfo);
            gatewayInfo.setGmtCreated(paasGatewayCredentials.get(0).getGmtCreate().getTime());
            gatewayInfos.add(gatewayInfo);
        }
        return gatewayInfos;
    }

    public PaasGatewayCredentials getById(Long id) {
        return paasGatewayCredentialsMapper.selectById(id);
    }

    public List<SubDeviceInfo> getPaasGwDevices(String gwId, Long spaceId, String tenantId) {
        List<SubDeviceInfo> deviceInfoDataList = new ArrayList<>();
        List<PaasGatewayDeviceRelation> list = paasGatewayDeviceRelationMapper.selectList(new LambdaQueryWrapper<PaasGatewayDeviceRelation>().
                eq(PaasGatewayDeviceRelation::getGwId, gwId).eq(PaasGatewayDeviceRelation::getTenantId, tenantId));
        for (PaasGatewayDeviceRelation relation : list){
            List<SaasDeviceInfo> deviceInfos = deviceInfoMapper.selectList(new LambdaQueryWrapper<SaasDeviceInfo>()
                    .eq(SaasDeviceInfo::getDeviceName, relation.getDeviceName()).eq(SaasDeviceInfo::getTenantId, tenantId));
            if(CollectionUtils.isEmpty(deviceInfos)){
                log.error("getGwDevices saasDeviceInfo is null, deviceName:{}", relation.getDeviceName());
                continue;
            }
            SaasDeviceInfo deviceInfo = deviceInfos.get(0);
            SubDeviceInfo deviceInfoData = new SubDeviceInfo();
            BeanUtils.copyProperties(deviceInfo, deviceInfoData);
            deviceInfoData.setProductKey(deviceInfo.getProductKey());
            List<SaasSpaceDeviceRelation> spaces = saasSpaceDeviceRelationMapper.selectList(new LambdaQueryWrapper<SaasSpaceDeviceRelation>()
                    .eq(SaasSpaceDeviceRelation::getDeviceName, relation.getDeviceName()).eq(SaasSpaceDeviceRelation::getSpaceInfoId, spaceId));
            if (!CollectionUtils.isEmpty(spaces)){
                deviceInfoData.setPosition(spaces.get(0).getPosition());
            }
            deviceInfoDataList.add(deviceInfoData);
        }
        return deviceInfoDataList;
    }

    public List<DeviceBasicData> joinList(Long gwId) {
        List<DeviceBasicData> deviceInfoDataList = new ArrayList<>();
        PaasGatewayCredentials paasGatewayCredentials = paasGatewayCredentialsMapper.selectById(gwId);
        if (paasGatewayCredentials == null){
            return deviceInfoDataList;
        }
        List<PaasGatewayDeviceRelation> relations = paasGatewayDeviceRelationMapper.selectList(
                new LambdaQueryWrapper<PaasGatewayDeviceRelation>().eq(PaasGatewayDeviceRelation::getGwId, paasGatewayCredentials.getGwId()));
        for (PaasGatewayDeviceRelation relation : relations){
            DeviceBasicData deviceBasicData = new DeviceBasicData();
            deviceBasicData.setCoreId(relation.getDeviceId());
            deviceBasicData.setName(relation.getDeviceName());
            deviceInfoDataList.add(deviceBasicData);
        }
        return deviceInfoDataList;
    }

    public Boolean bindSubDevice(Long gwId, DeviceBasicData deviceBasicData, String token) {
        List<SaasDeviceInfo> list = deviceInfoMapper.selectList(new LambdaQueryWrapper<SaasDeviceInfo>()
                .eq(SaasDeviceInfo::getDeviceName, deviceBasicData.getName())
                .eq(SaasDeviceInfo::getTenantId, deviceBasicData.getTenantId()));
        Long deviceId = null;
        if(CollectionUtils.isEmpty(list)){
            SaasDeviceInfo saasDeviceInfo = new SaasDeviceInfo();
            saasDeviceInfo.setDeviceName(deviceBasicData.getName());
            saasDeviceInfo.setTenantId(deviceBasicData.getTenantId());
            saasDeviceInfo.setCategoryId(deviceBasicData.getProfileId());
            CategroyResponse categroyResponse = categoryService.details(token, deviceBasicData.getProfileId());
            if(categroyResponse!=null){
                saasDeviceInfo.setCategoryName(categroyResponse.getCategoryName());
            }else {
                log.error("bindSubDevice categoryService categroyResponse is null error, profileId:{}", deviceBasicData.getProfileId());
                return false;
            }
            saasDeviceInfo.setDeviceAlias(deviceBasicData.getLabel());
            saasDeviceInfo.setType(DeviceTypeEnum.SUB_DEVICE.getCode());
            saasDeviceInfo.setGmtCreated(new Date());
            saasDeviceInfo.setGmtModified(new Date());
            if (deviceInfoMapper.insert(saasDeviceInfo) != 1){
                log.error("bindSubDevice insert saasDeviceInfo error, saasDeviceInfo:{}", saasDeviceInfo);
                return false;
            }
            deviceId = saasDeviceInfo.getId();
        }else {
            deviceId = list.get(0).getId();
        }
        SaasGatewayDeviceRelation saasGatewayDeviceRelation = new SaasGatewayDeviceRelation();
        saasGatewayDeviceRelation.setGwId(gwId);
        saasGatewayDeviceRelation.setSubDeviceId(deviceId);
        saasGatewayDeviceRelation.setDeviceName(deviceBasicData.getName());
        saasGatewayDeviceRelation.setGmtCreated(new Date());
        saasGatewayDeviceRelation.setGmtModified(new Date());
        if (saasGatewayDeviceRelationMapper.insert(saasGatewayDeviceRelation) != 1) {
            log.error("bindSubDevice insert saasGatewayDeviceRelation error, saasGatewayDeviceRelation:{}", saasGatewayDeviceRelation);
            return false;
        }
        return true;
    }

    public Boolean addSubDevice(Long gwId, DeviceBasicData deviceBasicData, String token) {
        List<SaasDeviceInfo> list = deviceInfoMapper.selectList(new LambdaQueryWrapper<SaasDeviceInfo>()
                .eq(SaasDeviceInfo::getDeviceName, deviceBasicData.getName())
                .eq(SaasDeviceInfo::getTenantId, deviceBasicData.getTenantId()));
        Long deviceId = null;
        if(CollectionUtils.isEmpty(list)){
            SaasDeviceInfo saasDeviceInfo = new SaasDeviceInfo();
            saasDeviceInfo.setDeviceName(deviceBasicData.getName());
            saasDeviceInfo.setTenantId(deviceBasicData.getTenantId());
            saasDeviceInfo.setCategoryId(deviceBasicData.getProfileId());
            CategroyResponse categroyResponse = categoryService.details(token, deviceBasicData.getProfileId());
            if(categroyResponse!=null){
                saasDeviceInfo.setCategoryName(categroyResponse.getCategoryName());
            }else {
                log.error("addSubDevice categoryService categroyResponse is null error, profileId:{}", deviceBasicData.getProfileId());
                return false;
            }
            saasDeviceInfo.setDeviceAlias(deviceBasicData.getLabel());
            saasDeviceInfo.setType(DeviceTypeEnum.SUB_DEVICE.getCode());
            saasDeviceInfo.setGmtCreated(new Date());
            saasDeviceInfo.setGmtModified(new Date());
            if (deviceInfoMapper.insert(saasDeviceInfo) != 1){
                log.error("addSubDevice insert saasDeviceInfo error, saasDeviceInfo:{}", saasDeviceInfo);
                return false;
            }
            deviceId = saasDeviceInfo.getId();
        }else {
            deviceId = list.get(0).getId();
        }
        SaasGatewayDeviceRelation saasGatewayDeviceRelation = new SaasGatewayDeviceRelation();
        saasGatewayDeviceRelation.setGwId(gwId);
        saasGatewayDeviceRelation.setSubDeviceId(deviceId);
        saasGatewayDeviceRelation.setDeviceName(deviceBasicData.getName());
        saasGatewayDeviceRelation.setGmtCreated(new Date());
        saasGatewayDeviceRelation.setGmtModified(new Date());
        if (saasGatewayDeviceRelationMapper.insert(saasGatewayDeviceRelation) != 1) {
            log.error("addSubDevice insert saasGatewayDeviceRelation error, saasGatewayDeviceRelation:{}", saasGatewayDeviceRelation);
            return false;
        }
        return true;
    }

    public Boolean unbindSubDevice(String gwId, String subDeviceId, String deviceName, String token) {
        boolean subFlag = rexolarApiClient.deleteRelationsInfo(token, gwId, "DEVICE", "Created", subDeviceId, "DEVICE");
        if (!subFlag) {
            log.error("unbindSubDevice deleteRelationsInfo error, subDeviceId:{}", subDeviceId);
        }
        saasGatewayDeviceRelationMapper.delete(new LambdaQueryWrapper<SaasGatewayDeviceRelation>()
                .eq(SaasGatewayDeviceRelation::getGwId, gwId).eq(SaasGatewayDeviceRelation::getDeviceName, deviceName));
        return true;
    }

    public Boolean deleteSubDevice(String gwId, String subDeviceId, String deviceName, String token) {
        boolean subFlag = rexolarApiClient.deleteRelationsInfo(token, gwId, "DEVICE", "Created", subDeviceId, "DEVICE");
        if (!subFlag) {
            log.error("deleteSubDevice deleteRelationsInfo error, subDeviceId:{}", subDeviceId);
        }
        boolean deleteFlag = rexolarApiClient.deleteDevice(subDeviceId, token);
        if (!deleteFlag) {
            log.error("deleteSubDevice deleteDevice error, subDeviceId:{}", subDeviceId);
        }
        saasGatewayDeviceRelationMapper.delete(new LambdaQueryWrapper<SaasGatewayDeviceRelation>()
                .eq(SaasGatewayDeviceRelation::getGwId, gwId).eq(SaasGatewayDeviceRelation::getDeviceName, deviceName));
        deviceInfoMapper.delete(new LambdaQueryWrapper<SaasDeviceInfo>()
                .eq(SaasDeviceInfo::getDeviceName, deviceName));
        return true;
    }

    public List<PaasGatewayDeviceRelation> getSubDeviceList(String gwId, String tenantId) {
        return paasGatewayDeviceRelationMapper.selectList(new LambdaQueryWrapper<PaasGatewayDeviceRelation>()
                        .eq(PaasGatewayDeviceRelation::getTenantId, tenantId)
                .eq(PaasGatewayDeviceRelation::getGwId, gwId));
    }
}
